
import parse
import readcontact


class VCARD:

    def __init__(self):
        self.__isdone = True
        self.__reader = None
        self.__fout = None

    def __init(self,fin,fout,version,verbose):
        self.version = version
        self.__makefilehead()
        self.__closefin()
        self.__closefout()
        self.__openfin(fin)
        self.__openfout(fout)
        self.__person = []
        self.__isdone = False
        self.__statistic=[]
        self.verbose=verbose

    def __openfin(self, path='./vcard.xlsx', mode='r'):
        self.__reader = VCARD.datareader(path)

    def __closefin(self):
        if self.__reader:
            self.__reader.close()
            self.__reader = None

    def __openfout(self, path='./vcard.vcf'):
        f = open(path, 'w')
        self.__fout = f

    def __closefout(self):
        if self.__fout:
            self.__fout.close()
            self.__fout = None

    def __makefilehead(self):
        self.versions = ['undefined', '2.1', '3.0', '4.0']
        idx = self.versions.index(self.version)

        if idx == 1:
            raise ValueError
        elif idx == 2:
            self.property = {
                'BEGIN:': 'VCARD',
                'VERSION:': self.version,
                'END:': 'VCARD'
            }
        elif idx == 3:
            raise ValueError
        else:
            raise ValueError

    def __get_head(self):
        version = self.property.get('BEGIN:')
        return 'BEGIN:'+version

    def __get_version(self):
        version = self.property.get('VERSION:')
        return 'VERSION:'+version

    def __get_end(self):
        version = self.property.get('END:')
        return 'END:'+version

    def __write(self):
        f = self.__fout

        ok=0
        count=0
        for person in self.__person:
            if not person.isok():
                if self.verbose:
                    print("line: "+str(count+2)+" (in .xlsx file) convert failed!")
            else:
                f.write(self.__get_head()+'\n')
                f.write(self.__get_version()+'\n')
                person.write(self.__fout)
                f.write(self.__get_end()+'\n')

                ok = ok+1
            
            count=count+1

        f.close()
        self.__statistic.append(ok)
        self.print_statistic()
        self.__isdone = True

    def print_statistic(self):
        print("Read "+str(self.__statistic[0])+' lines.')
        print("Convert "+str(self.__statistic[1])+' lines.')

    def convert(self,fin='./vcard.xlsx',fout='./vcard.vcf',version='3.0',verbose=False):
        if self.__isdone == True:
            self.__init(fin,fout,version,verbose)

        while True:
            newperson = VCARD.person(self.version)
            if not self.__reader.parse(newperson):
                break
            else:
                self.__person.append(newperson)
                # newperson.debugprint()

        self.__statistic.append(len(self.__person))
        self.__write()

    def print_version(self):
        pass

    class person:
        def __init__(self, version='3.0'):
            self.__version = version
            self.property = []
            self.__setproperty_defa()
            self.__isok = False
            self.__isutf8 = False

        def __setproperty_defa(self):
            self.name = []
            self.fullname = ''
            self.telephone = []

            self.__charset = {'utf8': 'UTF-8', 'default': ''}
            self.__codefmt = {'quopri': 'QUOTED-PRINTABLE:', 'default': ''}

        def __do_setfullname(self,name):
            self.fullname=name

        def __writefullname(self, fout):
            if self.__version == '3.0':
                fullname = 'FN:'
                if self.__isutf8:
                    fullname='FN;'
                fullname = fullname+self.fullname
                fout.write(fullname+'\n')
                return True
            else:
                raise ValueError

        def __do_setname(self, name, codedformat='quopri', characterset='utf8'):
            self.name = []
            if characterset != '':
                charset = 'CHARSET='+characterset
                codefmt = 'ENCODING='+codedformat
                self.name.append(charset)
                self.name.append(codefmt)
            elif characterset == '':
                if not self.fullname:
                    self.__do_setfullname(name)

                name = name.split(' ')
                while len(name) < 4:
                    name.append('')

            if codedformat == 'QUOTED-PRINTABLE:':
                name = parse.eleparse.str2quopri(name)
                self.name.append(name)
                if not self.fullname:
                    self.__do_setfullname(charset+';'+codefmt+name)
            else:
                for s in name:
                    self.name.append(s)

        def setname(self, name):
            charset = 'default'
            codefmt = 'default'

            if parse.eleparse.isContainChinese(name[1]):
                charset = 'utf8'
                codefmt = 'quopri'
                self.__isutf8 = True

            charset = self.__charset.get(charset, '')
            codefmt = self.__codefmt.get(codefmt, '')

            self.__do_setname(name[1], codefmt, charset)

        def __writename(self, fout):
            if self.__version == '3.0':
                name = 'N:'
                if self.__isutf8:
                    name = 'N;'
                    charset = self.name[0]
                    codedfmt = self.name[1]
                    name = name+charset+';'+codedfmt+';'+self.name[2]+';'
                else:
                    name = name+self.name[1]+';'+self.name[0] + \
                        ';'+self.name[2]+';'+self.name[3]+';'
                fout.write(name+'\n')
                return True
            else:
                raise ValueError

        def __do_settelephone(self, phonenumber, phonetype='CELL'):
            if type(phonetype) is not str:
                raise ValueError
            else:
                newnumber = {phonetype: phonenumber}

            self.telephone.append(newnumber)

        def settelephone(self, phonenumber):
            phonetype = ''

            if not phonetype:
                phonetype = 'CELL'

            self.__do_settelephone(phonenumber[1], phonetype)

        def __writetelephone(self, fout):
            if self.__version == '3.0':
                for teldict in self.telephone:
                    tel = 'TEL;'
                    teltype = list(teldict.keys())[0]
                    telval = teldict[teltype]
                    teltype = 'TYPE='+teltype+':'
                    tel = tel+teltype+telval
                    fout.write(tel+'\n')
                return True
            else:
                raise ValueError

        def write(self, fout):
            if not self.check():
                return False
            else:
                self.__writename(fout)
                self.__writefullname(fout)
                self.__writetelephone(fout)

        def isok(self):
            return self.__isok

        def check(self):
            def name_isempty(self):
                for val in self.name:
                    if val !='':
                        return True
                return False

            def tel_isempty(self):
                for dic in self.telephone:
                    if dic.values !='':
                        return True
                return False

            self.__isok = name_isempty(self) and tel_isempty(self)
            return self.__isok

        def debugprint(self):
            print("debug info:")
            print(self)
            print("name:"+str(self.name))
            print("fullname:"+self.fullname)
            print("tel:"+str(self.telephone))

    class dataconverter:
        def __init__(self):
            self.__initfunctb()

        def __initfunctb(self):
            self.__converttable = {
                '姓名': VCARD.person.setname,
                '名字': VCARD.person.setname,
                '电话': VCARD.person.settelephone,
                '電話': VCARD.person.settelephone,
                '手机': VCARD.person.settelephone,
                '手機': VCARD.person.settelephone,
                'name': VCARD.person.setname,
                'Name': VCARD.person.setname,
                'tel': VCARD.person.settelephone,
                'Tel': VCARD.person.settelephone,
                'TEL': VCARD.person.settelephone,
            }

        def key2fun(self, key):
            return self.__converttable.get(key)

    class datareader:
        def __init__(self, path='./vcard.xlsx'):
            self.__isok = False
            self.__srcpath = path
            self.__loadxlsx()
            self.__converter = VCARD.dataconverter()
            self.__assigntable = []
            self.__lineidx = 1
            self.__line = []

        def __closexlsx(self):
            self.__srcxlsx.close()

        def __loadxlsx(self):
            self.__srcxlsx = readcontact.ReadXLSX(self.__srcpath)

        def __getline(self):
            self.__line = self.__srcxlsx.readsheet(
                self.__lineidx, 1, readline=True)
            if not self.__line:
                return False
            else:
                self.__lineidx = self.__lineidx+1
                return True

        def __parsehead(self):
            self.__head = self.__srcxlsx.readsheet(1, 1, readline=True)

            for key in self.__head:
                func = self.__converter.key2fun(key)
                self.__assigntable.append(func)

            self.__lineidx = 2

        def __parseperson(self):
            idx = 0
            for key in self.__line:
                func = self.__assigntable[idx]

                if not func:
                    # just skip it
                    pass
                else:
                    func(self.__person, [self.__head[idx], key])

                idx = idx+1

            return True

        def __doparse(self):
            if self.__person.isok():
                raise ValueError

            self.__parseperson()
            self.__person.check()
            self.__person = None
            return True

        def close(self):
            self.__closexlsx()

        def parse(self, target):
            if self.__isok == False:
                self.__parsehead()
                self.__isok = True

            if not self.__getline():
                return False

            if isinstance(target, VCARD.person) == False:
                raise ValueError
            else:
                self.__person = target

            self.__doparse()

            return True
